#ifndef FILE_HELPER_H_
#define FILE_HELPER_H_
#include <fstream>
#include <functional>
#include <memory>
#include <vector>

using IfStreamUniquePtr =
    std::unique_ptr<std::ifstream, std::function<void(std::ifstream*)>>;

using OfStreamUniquePtr =
    std::unique_ptr<std::ofstream, std::function<void(std::ofstream*)>>;

inline IfStreamUniquePtr make_ifstream_unique_ptr(const std::string& path) {
  IfStreamUniquePtr finPtr{new std::ifstream(path), [&](std::ifstream* p) {
                             if (p != nullptr) p->close();
                           }};
  return finPtr;
}

inline OfStreamUniquePtr make_ofstream_unique_Ptr(const std::string& path) {
  OfStreamUniquePtr foutPtr{new std::ofstream(path), [&](std::ofstream* p) {
                              if (p != nullptr) p->close();
                            }};
  return foutPtr;
}

///@brief is file exist
inline bool is_file_exist(const std::string& path) noexcept {
  auto const& finPtr{make_ifstream_unique_ptr(path)};
  if (finPtr != nullptr) {
    return finPtr->good();
  } else {
    return false;
  }
}

inline std::streampos get_binfile_size(std::string path) noexcept {
  std::streampos fileSize;
  auto const& finPtr{make_ifstream_unique_ptr(path)};
  finPtr->seekg(0, finPtr->end);
  fileSize = finPtr->tellg();
  return fileSize;
}

inline void read_bin_file(std::string path, char* buf, int size) noexcept {
  auto const& finPtr{make_ifstream_unique_ptr(path)};
  finPtr->read(buf, size);
}

inline void write_bin_file(std::string path, char* buf, int size) noexcept {
  auto const& foutPtr{make_ofstream_unique_Ptr(path)};
  foutPtr->write(buf, size);
}

template <typename T>
inline void read_bin_file(std::string path, std::vector<T>& data) noexcept {
  auto binSize{get_binfile_size(path)};
  auto vectorSize{binSize / sizeof(T)};
  data.resize(vectorSize);
  read_bin_file(path, reinterpret_cast<char*>(data.data()), binSize);
}

#endif